package requests

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

type Requests struct {
	Url              string
	HeaderMap        map[string]string // 请求头Map
	FilePath         string            // 下载文件保存路径
	ParamMap         map[string]string // 参数Map
	ParamJson        string            // JSON 字符串，有此项则ParamMap将不会奏效
	bodyBytes        []byte            //  请求Body
	Proxy            string            // （目前仅支持Http代理）代理参数：http://127.0.0.1:1087 或者 socks://127.0.0.1:1080
	PostType         string            // post 使用类型
	client           *http.Client
	Written          int64         // 已经下载完成的进度
	TotalFileLength  int64         //  总的文件大小
	formDataBoundary string        //  FORM 边界分隔符
	req              *http.Request // 请求暂存处。
	Locations        []string      // 所有请求的路径
}

const (
	PROXY_HTTP          = "HTTP"
	PROXY_SOCKS         = "SOCKS"
	POST_TYPE_JSON      = "JSON"
	POST_TYPE_FORM_DATA = "FORM_DATA"
	POST_TYPE_URL_PARAM = "URL_PARAM"
	DEFAULT_USER_AGENT  = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
)

func (c *Requests) SetUrl(url string) *Requests {
	c.Url = url
	return c
}

func (c *Requests) UseJson() *Requests {
	c.PostType = POST_TYPE_JSON
	c.AddHeader("Content-Type", "application/json")

	return c
}

func (c *Requests) UseFormData() *Requests {
	c.PostType = POST_TYPE_FORM_DATA
	return c
}

func (c *Requests) UserIPhoneUserAgent() *Requests {
	c.AddHeader("User-Agent", DEFAULT_USER_AGENT)
	return c
}

func (c *Requests) SetHeaders(headerMap map[string]string) *Requests {
	if c.HeaderMap != nil {
		for k, v := range headerMap {
			c.HeaderMap[k] = v
		}
	} else {
		c.HeaderMap = headerMap
	}
	return c
}

/*
*
设置Header
*/
func (c *Requests) AddHeader(k, v string) *Requests {
	if c.HeaderMap == nil {
		c.HeaderMap = make(map[string]string)
	}
	c.HeaderMap[k] = v
	return c
}

/*
*
设置请求UserAgent
*/
func (c *Requests) SetUserAgent(userAgent string) *Requests {
	if c.HeaderMap == nil {
		c.HeaderMap = make(map[string]string)
	}
	c.HeaderMap["User-Agent"] = userAgent
	return c
}

/*
*
设置参数
*/
func (c *Requests) SetParams(params map[string]string) *Requests {
	if c.ParamMap != nil {
		for k, v := range params {
			c.ParamMap[k] = v
		}
	} else {
		c.ParamMap = params
	}
	return c
}

/*
*
添加参数
*/
func (c *Requests) AddParams(k, v string) *Requests {
	if c.ParamMap == nil {
		c.ParamMap = make(map[string]string)
	}
	c.ParamMap[k] = v
	return c
}

func (c *Requests) MakeCommonClient() (err error) {
	var tr = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	if c.Proxy != "" {
		// 开始执行Http的代理配置
		proxyUrl, err := url.Parse("http://proxyIp:proxyPort")
		if err != nil {
			return err
		}
		tr.Proxy = http.ProxyURL(proxyUrl)
	}
	c.client = &http.Client{Transport: tr, CheckRedirect: func(req *http.Request, via []*http.Request) error {

		c.Locations = append(c.Locations, req.URL.String())
		//return http.ErrUseLastResponse
		return nil
	}}
	return nil
}

// 该方法仅仅在GET的时候使用（也就是在下载，或者Get的时候使用）
func (c *Requests) MakeFullUrl(targetUrl string) {
	if targetUrl != "" {
		c.Url = targetUrl
	}
	if c.ParamMap != nil {
		first := true
		for k, v := range c.ParamMap {
			if strings.Contains(c.Url, "?") {
				c.Url = fmt.Sprintf("%s&%s=%s", c.Url, k, url.QueryEscape(v))
			} else {
				if first {
					c.Url = fmt.Sprintf("%s?%s=%s", c.Url, k, url.QueryEscape(v))
					first = false
				} else {
					c.Url = fmt.Sprintf("%s&%s=%s", c.Url, k, url.QueryEscape(v))
				}
			}
		}
	}
}

func (c *Requests) MakeFullSavePath(targetUrl, savePath, saveFileName, headerRespFileName string) (fullSavePath string) {
	if saveFileName == "" {
		saveFileName = headerRespFileName
	}
	if saveFileName == "" {
		// 开始通过URL获取最后一截来作为保存文件名
		items := strings.Split(targetUrl, "?")
		pureUrl := items[0]
		lastIdx := strings.LastIndex(pureUrl, "/")
		if lastIdx > 0 && lastIdx < len(pureUrl) {
			saveFileName = pureUrl[lastIdx:]
		}
	}
	if !strings.HasSuffix(savePath, "/") || !strings.HasSuffix(savePath, "\\") {
		savePath += "/"
	}
	return savePath + saveFileName
}

func (c *Requests) GetFileNameFromUrl(targeturl string) string {
	items := strings.Split(targeturl, "?")
	pureUrl := items[0]
	lastIdx := strings.LastIndex(pureUrl, "/")
	if lastIdx > 0 && lastIdx < len(pureUrl) {
		return pureUrl[lastIdx:]
	}
	return ""
}

func (c *Requests) GetFileNameFromLocalFilePath(localPath string) (file string) {
	_, file = filepath.Split(localPath)
	return
}

func (c *Requests) GetFileFromContentDisposition(contentDisposition string) (filename string) {
	items := strings.Split(contentDisposition, ";")
	for _, item := range items {
		kvPair := strings.Split(item, "=")
		if len(kvPair) == 2 {
			if kvPair[0] == "filename" {
				filename = kvPair[1]
			}
		}
	}
	return filename
}

func (c *Requests) SetupHeader() {
	if c.HeaderMap != nil {
		for k, v := range c.HeaderMap {
			c.req.Header.Set(k, v)
		}
	}
}

// Download 重写下载，如果服务支持多线程下载则优先使用多线程下载，否则直接使用单线程下载
func (c *Requests) Download(targetUrl, savePath, saveFileName string, useMultiThread bool) (fullPath string, err error) {
	if !useMultiThread {
		return c.DownloadSingleThread(targetUrl, savePath, saveFileName)
	} else {
		if contentLength, supportMultiThread, err := c.GetContentLength(targetUrl); err == nil {
			if supportMultiThread {
				var threadGroup = sync.WaitGroup{}
				// 获取到了相关的文件名
				fmt.Println("获取到的文件的长度为：", contentLength)
				// 开始执行分片下载
				fullPath = c.MakeFullSavePath(targetUrl, savePath, saveFileName, "")
				file, err := os.Create(fullPath)
				if err != nil {
					return "", err
				}
				// 先获取系统的cpu数量，并开始平均分配cpu-1的线程，并通过线程分解数量
				cpuNum := runtime.NumCPU()
				availableCpuNum := cpuNum - 1
				// 开始计算每个线程所需要下载包的大小
				// 因为大概率是无法整除的，所以直接使用可用Cpu-1来进行除，获取到的包大小
				// 然后还有多余的包的话，那就执行分片下载。
				packageSize := availableCpuNum / cpuNum
				for count := 0; count < availableCpuNum; count++ {
					var start int64
					var end int64
					start = int64(count * packageSize)
					end = start + int64(packageSize)
					if end > contentLength {
						end = end - (end - contentLength)
					}
					if req, err := http.NewRequest("GET", targetUrl, nil); err == nil {
						if c.HeaderMap != nil {
							for k, v := range c.HeaderMap {
								req.Header.Set(k, v)
							}
						}
						req.Header.Set("Range", "bytes="+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10))
						threadGroup.Add(1)
						go func(req *http.Request, file *os.File, start int64) {
							defer threadGroup.Done()
							if resp, err := c.client.Do(req); err == nil {
								defer resp.Body.Close()
								if bytes, e := io.ReadAll(resp.Body); e == nil {
									file.WriteAt(bytes, start)
								}
							}
						}(req, file, start)
					}
				}
				threadGroup.Wait()
				return "", err
			} else {
				return c.DownloadSingleThread(targetUrl, savePath, saveFileName)
			}
		} else {
			return "", err
		}
	}
	return
}

// DownloadSingleThread 下载数据方法
func (c *Requests) DownloadSingleThread(targetUrl, savePath, saveFileName string) (fullPath string, err error) {
	// 默认以请求方法的URL覆盖最开始设置的URL
	c.MakeFullUrl(targetUrl)
	// 设置请求客户端
	if err = c.MakeCommonClient(); err != nil {
		return "", err
	}
	fullPath = savePath + savePath
	req, err := http.NewRequest("GET", c.Url, nil)
	if err != nil {
		return "", err
	}
	// 设置请求头实体类
	if c.HeaderMap != nil {
		for k, v := range c.HeaderMap {
			req.Header.Set(k, v)
		}
	}
	resp, err := c.client.Do(req)
	if err != nil {
		return "", err
	}
	// 就先释放了。
	defer resp.Body.Close()
	contentDisposition := resp.Header.Get("content-disposition")
	if contentDisposition == "" {
		resp.Header.Get("Content-Disposition")
	}
	remoteHeaderFileName := c.GetFileFromContentDisposition(contentDisposition)
	fullPath = c.MakeFullSavePath(targetUrl, savePath, saveFileName, remoteHeaderFileName)
	reader := bufio.NewReaderSize(resp.Body, 32*1024)
	file, err := os.Create(fullPath)
	if err != nil {
		return "", err
	}
	writer := bufio.NewWriter(file)
	c.Written, err = io.Copy(writer, reader)
	if err != nil {
		return "", err
	}
	return
}

func (c *Requests) GetContentLength(targetUrl string) (contentLength int64, supportMultiThread bool, err error) {
	c.MakeFullUrl(targetUrl)
	if err = c.MakeCommonClient(); err != nil {
		return contentLength, supportMultiThread, err
	}
	if req, err := http.NewRequest("HEAD", c.Url, nil); err == nil {
		if resp, err := c.client.Do(req); err == nil {
			defer resp.Body.Close()
			contentLength = resp.ContentLength
			if strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
				supportMultiThread = true
			}
			return contentLength, supportMultiThread, nil
		}
	}
	return
}

func (c *Requests) Upload(targetUrl, fileParamName, fileName, uploadFilePath string) (result string, err error) {
	// 设置文件的默认参数名称
	if fileParamName == "" {
		fileParamName = "file"
	}
	if fileName == "" {
		fileName = c.GetFileNameFromLocalFilePath(uploadFilePath)
	}
	if fileName == "" {
		fileName = c.GetFileNameFromUrl(targetUrl)
	}
	if fileName == "" {
		fileName = "unnamed.file"
	}
	// 上传文件
	file, err := os.Open(uploadFilePath)
	if err != nil {
		return "", err
	}
	defer file.Close()
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(fileParamName, fileName)
	_, err = io.Copy(part, file)
	if err != nil {
		return "", err
	}
	for k, v := range c.ParamMap {
		writer.WriteField(k, v)
	}
	err = writer.Close()
	if err != nil {
		return "", err
	}
	c.req, err = http.NewRequest("POST", targetUrl, body)
	if err != nil {
		return "", err
	}
	c.SetupHeader()
	c.req.Header.Set("Content-Type", writer.FormDataContentType())
	if err = c.MakeCommonClient(); err != nil {
		return "", err
	}
	if resp, err := c.client.Do(c.req); err == nil {
		if body, err := io.ReadAll(resp.Body); err == nil {
			result = string(body)
		}
	}
	return
}

func (c *Requests) Get(targetUrl string) (result string, err error) {
	c.MakeFullUrl(targetUrl)
	fmt.Println("访问的URL为：" + c.Url)
	// 设置请求客户端
	if err = c.MakeCommonClient(); err != nil {
		return "", err
	}
	req, err := http.NewRequest("GET", c.Url, nil)
	if err != nil {
		return "", err
	}
	// 设置请求头实体类
	if c.HeaderMap != nil {
		for k, v := range c.HeaderMap {
			req.Header.Set(k, v)
		}
	}
	resp, err := c.client.Do(req)
	if err != nil {
		return "", err
	}
	if body, err := io.ReadAll(resp.Body); err == nil {
		result = string(body)
	}
	// 关闭请求资源，并释放资源
	err = resp.Body.Close()
	if err != nil {
		return "", err
	}
	return
}

// 单独的post方法将不支持文件上传
func (c *Requests) Post(targetUrl string) (result string, err error) {
	if targetUrl != "" {
		c.Url = targetUrl
	}
	if err = c.MakeCommonClient(); err != nil {
		return
	}
	// 设置默认为JSON推送
	if c.PostType == "" {
		c.UseJson()
	}
	var bodyBuffer *bytes.Buffer
	if c.PostType == POST_TYPE_JSON {
		if c.ParamJson == "" {
			if jsonBytes, err := json.Marshal(c.ParamMap); err == nil {
				c.bodyBytes = jsonBytes
			} else {
				return "", err
			}

		} else {
			c.bodyBytes = []byte(c.ParamJson)
		}
		bodyBuffer = bytes.NewBuffer(c.bodyBytes)
	} else if c.PostType == POST_TYPE_FORM_DATA {
		writer := multipart.NewWriter(bodyBuffer)
		if c.ParamMap != nil {
			for k, v := range c.ParamMap {
				writer.WriteField(k, v)
			}
		}
		if err = writer.Close(); err != nil {
			return "", err
		}
		c.AddHeader("Content-Type", writer.FormDataContentType())
	}
	req, err := http.NewRequest("POST", c.Url, bodyBuffer)
	if err != nil {
		return "", err
	}
	// 设置请求头实体类
	if c.HeaderMap != nil {
		for k, v := range c.HeaderMap {
			req.Header.Set(k, v)
		}
	}
	resp, err := c.client.Do(req)
	if err != nil {
		return "", err
	}
	if body, err := io.ReadAll(resp.Body); err == nil {
		result = string(body)
	}
	// 关闭请求资源，并释放资源
	err = resp.Body.Close()
	if err != nil {
		return "", err
	}
	return
}

func (c *Requests) Put(targetUrl string) {

}

func (c *Requests) Delete(targetUrl string) {

}
